package tge

import (
	"net"
	"sync"
	"time"
	"strings"
	"runtime/debug"
)

type Server struct {
	Conns		map[string]net.Conn
	Parser		*MsgParser
	CreateAgent	func(Conn) Agent

	IsStarted	bool
	listener	net.Listener
	mu 			sync.RWMutex
	wg 			sync.WaitGroup
}

func NewServer() *Server {
	s := &Server{}
	s.Conns		= make(map[string]net.Conn)
	s.Parser 	= NewMsgParser()
	s.IsStarted = false
	return s
}

func (s *Server)handlerServer(conn net.Conn) {
	defer func() {
		if err := recover(); err != nil {
			logger.Println("handlerServer", err, string(debug.Stack()))
		}
	}()

	defer conn.Close()

	s.wg.Add(1)
	defer s.wg.Done()

	logger.Println("Accept connect from", conn.RemoteAddr().String())
	agent := s.CreateAgent(NewTcpConn(conn, s.Parser))
	defer agent.Close()
	agent.Run()
	
	s.mu.Lock()
	delete(s.Conns, conn.RemoteAddr().String())
	s.mu.Unlock()
	logger.Println("close connect from", conn.RemoteAddr().String())
}

func (s *Server)Send(to string, msg []byte) error {
	s.mu.RLock()
	conn, ok := s.Conns[to]
	s.mu.RUnlock()
	if ok {
		_, err := s.Parser.Write(conn, msg)
		return err
	}
	return nil
}

func (s *Server)StartServer(port string) {
	if s.IsStarted {
		logger.Println("Server is started!")
		return
	}

	host := ":" + port
	if strings.Contains(port, ":") {
		host = port
	}
	addr, err := net.ResolveTCPAddr("tcp4", host)
	if err != nil {
		logger.Println(err)
		return
	}

	ln, err := net.ListenTCP("tcp", addr)
	if err != nil {
		logger.Println(err)
		return
	}
	s.listener = ln
	defer s.listener.Close()

	s.IsStarted = true

	logger.Println("Start Server on", port)

	for {
		conn, err := ln.Accept()
		if err != nil {
			logger.Println(err)
			break
		} else {
			s.mu.Lock()
			s.Conns[conn.RemoteAddr().String()] = conn
			s.mu.Unlock()
			// conn.RemoteAddr().String()
			go s.handlerServer(conn)
		}
	}

	s.mu.Lock()
	t := time.Now().Add(100 * time.Millisecond)
	for _, c := range s.Conns {
		c.SetReadDeadline(t)
	}
	s.mu.Unlock()

	s.IsStarted = false
}

func (s *Server)Close() {
	if !s.IsStarted {
		logger.Println("Server isn't started")
		return
	}

	if err := s.listener.Close(); err != nil {
		logger.Println("Close listener error:", err)
	}

	logger.Println("waiting for threads end")
	s.wg.Wait()

	logger.Println("Server is closed")
}